"git2-curl 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"hamcrest 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "handlebars 0.20.5 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
"libgit2-sys 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
]
-[[package]]
-name = "handlebars"
-version = "0.20.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
[[package]]
name = "idna"
version = "0.1.0"
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
-[[package]]
-name = "lazy_static"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
[[package]]
name = "lazy_static"
version = "0.2.2"
"user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
-[[package]]
-name = "pest"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
[[package]]
name = "pkg-config"
version = "0.3.9"
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
-[[package]]
-name = "quick-error"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "quote"
-version = "0.3.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
[[package]]
name = "rand"
version = "0.3.15"
"checksum git2-curl 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "68676bc784bf0bef83278898929bf64a251e87c0340723d0b93fa096c9c5bf8e"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum hamcrest 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bf088f042a467089e9baa4972f57f9247e42a0cc549ba264c7a04fbb8ecb89d4"
-"checksum handlebars 0.20.5 (registry+https://github.com/rust-lang/crates.io-index)" = "07f9c1d28bcfb97143c95ed0667141677b2b5675c7ba3d5b81459ad43b1073bd"
"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-"checksum lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417"
"checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b"
"checksum libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "684f330624d8c3784fb9558ca46c4ce488073a8d22450415c5eb4f4cfb0d11b5"
"checksum libgit2-sys 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d951fd5eccae07c74e8c2c1075b05ea1e43be7f8952245af8c2840d1480b1d95"
"checksum num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a225d1e2717567599c24f88e49f00856c6e825a12125181ee42c4257e3688d39"
"checksum openssl 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0c00da69323449142e00a5410f0e022b39e8bbb7dc569cee8fc6af279279483c"
"checksum openssl-probe 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "756d49c8424483a3df3b5d735112b4da22109ced9a8294f1f5cdf80fb3810919"
-"checksum openssl-sys 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b1482f9a06f56c906007e17ea14d73d102210b5d27bc948bf5e175f493f3f7c3"
-"checksum pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0a6dda33d67c26f0aac90d324ab2eb7239c819fc7b2552fe9faa4fe88441edc8"
-"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
+"checksum openssl-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "95e9fb08acc32509fac299d6e5f4932e1e055bb70d764282c3ed8beaa87ab0e9"
+"checksum pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8cee804ecc7eaf201a4a207241472cc870e825206f6c031e3ee2a72fa425f2fa"
"checksum psapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "abcd5d1a07d360e29727f757a9decb3ce8bc6e0efa8969cfaad669a8317a2478"
-"checksum quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0aad603e8d7fb67da22dbdf1f4b826ce8829e406124109e73cf1b2454b93a71c"
-"checksum quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b44fd83db28b83c1c58187159934906e5e955c812e211df413b76b03c909a5"
-"checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d"
+"checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5"
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
"checksum rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)" = "bff9fc1c79f2dec76b253273d07682e94a978bd8f132ded071188122b2af9818"
git2 = "0.6"
git2-curl = "0.7"
glob = "0.2"
-handlebars = "0.20"
libc = "0.2"
libgit2-sys = "0.6"
log = "0.3"
flag_lib: bool,
arg_path: Option<String>,
flag_name: Option<String>,
- flag_template_subdir: Option<String>,
- flag_template: Option<String>,
flag_vcs: Option<ops::VersionControl>,
flag_frozen: bool,
flag_locked: bool,
--bin Use a binary (application) template
--lib Use a library template
--name NAME Set the resulting package name
- --template <repository> Use a specified template repository
- --template-subdir <template> Use a specified template within a template repository
-v, --verbose ... Use verbose output (-vv very verbose/build.rs output)
-q, --quiet No output printed to stdout
--color WHEN Coloring: auto, always, never
options.flag_frozen,
options.flag_locked)?;
- let Options {
- flag_bin, flag_lib,
- arg_path, flag_name,
- flag_vcs,
- flag_template_subdir, flag_template,
- ..
- } = options;
+ let Options { flag_bin, flag_lib, arg_path, flag_name, flag_vcs, .. } = options;
let tmp = &arg_path.unwrap_or(format!("."));
let opts = ops::NewOptions::new(flag_vcs,
flag_bin,
flag_lib,
tmp,
- flag_name.as_ref().map(|s| s.as_ref()),
- flag_template_subdir.as_ref().map(|s| s.as_ref()),
- flag_template.as_ref().map(|s| s.as_ref()));
+ flag_name.as_ref().map(|s| s.as_ref()));
let opts_lib = opts.lib;
ops::init(opts, config)?;
flag_lib: bool,
arg_path: String,
flag_name: Option<String>,
- flag_template_subdir: Option<String>,
- flag_template: Option<String>,
flag_vcs: Option<ops::VersionControl>,
flag_frozen: bool,
flag_locked: bool,
--bin Use a binary (application) template
--lib Use a library template
--name NAME Set the resulting package name, defaults to the value of <path>
- --template <repository> Use a specified template repository
- --template-subdir <template-subdir> Use a specified template within a template repository
-v, --verbose ... Use verbose output (-vv very verbose/build.rs output)
-q, --quiet No output printed to stdout
--color WHEN Coloring: auto, always, never
options.flag_frozen,
options.flag_locked)?;
- let Options {
- flag_bin, flag_lib,
- arg_path, flag_name,
- flag_vcs,
- flag_template_subdir, flag_template,
- ..
- } = options;
+ let Options { flag_bin, flag_lib, arg_path, flag_name, flag_vcs, .. } = options;
let opts = ops::NewOptions::new(flag_vcs,
flag_bin,
flag_lib,
&arg_path,
- flag_name.as_ref().map(|s| s.as_ref()),
- flag_template_subdir.as_ref().map(|s| s.as_ref()),
- flag_template.as_ref().map(|s| s.as_ref()));
+ flag_name.as_ref().map(|s| s.as_ref()));
let opts_lib = opts.lib;
ops::new(opts, config)?;
extern crate fs2;
extern crate git2;
extern crate glob;
-extern crate handlebars;
extern crate libc;
extern crate libgit2_sys;
extern crate num_cpus;
use std::env;
-use std::fs::{self, DirEntry, File};
-use std::path::{Path, PathBuf};
+use std::fs;
+use std::path::Path;
use std::collections::BTreeMap;
+
use rustc_serialize::{Decodable, Decoder};
use git2::Config as GitConfig;
use term::color::BLACK;
-use handlebars::{Handlebars, Context, no_escape};
-use tempdir::TempDir;
-
use core::Workspace;
-use sources::git::clone;
use util::{GitRepo, HgRepo, CargoResult, human, ChainError, internal};
-use util::{Config, paths, template};
-use util::template::{TemplateSet, TemplateFile, TemplateDirectory, TemplateType};
-use util::template::{InputFileTemplateFile, InMemoryTemplateFile, get_template_type};
+use util::{Config, paths};
+
+use toml;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum VersionControl { Git, Hg, NoVcs }
pub lib: bool,
pub path: &'a str,
pub name: Option<&'a str>,
- pub template_subdir: Option<&'a str>,
- pub template: Option<&'a str>,
}
struct SourceFileInformation {
struct MkOptions<'a> {
version_control: Option<VersionControl>,
- template_subdir: Option<&'a str>,
- template: Option<&'a str>,
path: &'a Path,
name: &'a str,
+ source_files: Vec<SourceFileInformation>,
bin: bool,
}
bin: bool,
lib: bool,
path: &'a str,
- name: Option<&'a str>,
- template_subdir: Option<&'a str>,
- template: Option<&'a str>) -> NewOptions<'a> {
+ name: Option<&'a str>) -> NewOptions<'a> {
// default to lib
let is_lib = if !bin {
lib: is_lib,
path: path,
name: name,
- template_subdir: template_subdir,
- template: template,
}
}
}
version_control: Option<VersionControl>,
}
-fn get_input_template(config: &Config, opts: &MkOptions) -> CargoResult<TemplateSet> {
- let name = opts.name;
-
- let template_type = try!(get_template_type(opts.template, opts.template_subdir));
- let template_set = match template_type {
- // given template is a remote git repository & needs to be cloned
- TemplateType::GitRepo(repo_url) => {
- let template_dir = try!(TempDir::new(name));
- config.shell().status("Cloning", &repo_url)?;
- clone(&repo_url, &template_dir.path(), &config)?;
- let template_path = find_template_subdir(&template_dir.path(), opts.template_subdir);
- TemplateSet {
- template_dir: Some(TemplateDirectory::Temp(template_dir)),
- template_files: try!(collect_template_dir(&template_path, opts.path))
- }
- },
- // given template is a local directory
- TemplateType::LocalDir(directory) => {
- // make sure that the template exists
- if fs::metadata(&directory).is_err() {
- bail!("template `{}` not found", directory);
- }
- let template_path = find_template_subdir(&PathBuf::from(&directory),
- opts.template_subdir);
- TemplateSet {
- template_dir: Some(TemplateDirectory::Normal(PathBuf::from(directory))),
- template_files: try!(collect_template_dir(&template_path, opts.path))
- }
- },
- // no template given, use either "lib" or "bin" templates depending on the
- // presence of the --bin flag.
- TemplateType::Builtin => {
- let template_files = if opts.bin {
- create_bin_template()
- } else {
- create_lib_template()
- };
- TemplateSet {
- template_dir: None,
- template_files: template_files
- }
- }
- };
- Ok(template_set)
-}
-
fn get_name<'a>(path: &'a Path, opts: &'a NewOptions, config: &Config) -> CargoResult<&'a str> {
if let Some(name) = opts.name {
return Ok(name);
let mkopts = MkOptions {
version_control: opts.version_control,
- template_subdir: opts.template_subdir,
- template: opts.template,
path: &path,
name: name,
+ source_files: vec![plan_new_source_file(opts.bin, name.to_string())],
bin: opts.bin,
};
let mkopts = MkOptions {
version_control: version_control,
- template_subdir: opts.template_subdir,
- template: opts.template,
path: &path,
name: name,
bin: src_paths_types.iter().any(|x|x.bin),
+ source_files: src_paths_types,
};
mk(config, &mkopts).chain_error(|| {
let (author_name, email) = discover_author()?;
// Hoo boy, sure glad we've got exhaustivenes checking behind us.
- let author = match (cfg.name.clone(), cfg.email.clone(), author_name, email) {
+ let author = match (cfg.name, cfg.email, author_name, email) {
(Some(name), Some(email), _, _) |
(Some(name), None, _, Some(email)) |
(None, Some(email), name, _) |
(None, None, name, None) => name,
};
- // construct the mapping used to populate the template
- // if in the future we want to make more varaibles available in
- // the templates, this would be the place to do it.
- let mut handlebars = Handlebars::new();
- // We don't want html escaping unless users explicitly ask for it...
- handlebars.register_escape_fn(no_escape);
- handlebars.register_helper("toml-escape", Box::new(template::toml_escape_helper));
- handlebars.register_helper("html-escape", Box::new(template::html_escape_helper));
-
- let mut data = BTreeMap::new();
- data.insert("name".to_owned(), name.to_owned());
- data.insert("author".to_owned(), author);
- data.insert("year".to_owned(), Local::now().year().to_string());
-
- let template_set = try!(get_input_template(config, opts));
- for template in template_set.template_files.iter() {
- let template_str = try!(template.template());
- let dest_path = path.join(template.path());
-
- // Skip files that already exist.
- if fs::metadata(&dest_path).is_ok() {
- continue;
+ let mut cargotoml_path_specifier = String::new();
+
+ // Calculare what [lib] and [[bin]]s do we need to append to Cargo.toml
+
+ for i in &opts.source_files {
+ if i.bin {
+ if i.relative_path != "src/main.rs" {
+ cargotoml_path_specifier.push_str(&format!(r#"
+[[bin]]
+name = "{}"
+path = {}
+"#, i.target_name, toml::Value::String(i.relative_path.clone())));
+ }
+ } else {
+ if i.relative_path != "src/lib.rs" {
+ cargotoml_path_specifier.push_str(&format!(r#"
+[lib]
+name = "{}"
+path = {}
+"#, i.target_name, toml::Value::String(i.relative_path.clone())));
+ }
+ }
+ }
+
+ // Create Cargo.toml file with necessary [lib] and [[bin]] sections, if needed
+
+ paths::write(&path.join("Cargo.toml"), format!(
+r#"[package]
+name = "{}"
+version = "0.1.0"
+authors = [{}]
+
+[dependencies]
+{}"#, name, toml::Value::String(author), cargotoml_path_specifier).as_bytes())?;
+
+
+ // Create all specified source files
+ // (with respective parent directories)
+ // if they are don't exist
+
+ for i in &opts.source_files {
+ let path_of_source_file = path.join(i.relative_path.clone());
+
+ if let Some(src_dir) = path_of_source_file.parent() {
+ fs::create_dir_all(src_dir)?;
}
- let parent = try!(dest_path.parent()
- .chain_error(|| {
- human(format!("failed to make sure parent directory \
- exists for {}", dest_path.display()))
- }));
- try!(fs::create_dir_all(&parent)
- .chain_error(|| {
- human(format!("failed to create path to destination file {}",
- parent.display()))
- }));
-
- // create the new file & render the template to it
- let mut dest_file = try!(File::create(&dest_path).chain_error(|| {
- human(format!("failed to open file for writing: {}",
- dest_path.display()))
- }));
-
- try!(handlebars.template_renderw(&template_str, &Context::wraps(&data), &mut dest_file)
- .chain_error(|| {
- human(format!("Failed to render template for file: {}", dest_path.display()))
- }))
+ let default_file_content : &[u8] = if i.bin {
+ b"\
+fn main() {
+ println!(\"Hello, world!\");
+}
+"
+ } else {
+ b"\
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn it_works() {
+ }
+}
+"
+ };
+
+ if !fs::metadata(&path_of_source_file).map(|x| x.is_file()).unwrap_or(false) {
+ paths::write(&path_of_source_file, default_file_content)?;
+ }
}
if let Err(e) = Workspace::new(&path.join("Cargo.toml"), config) {
workspace configuration\n\n{}", e);
config.shell().warn(msg)?;
}
- Ok(())
-}
-
-// When the command line has --template=<repository-or-directory> and
-// --template-subdir=<template-name> then find_template_subdir fixes up the name as appropriate.
-fn find_template_subdir(template_dir: &Path, template: Option<&str>) -> PathBuf {
- match template {
- Some(template) => template_dir.join(template),
- None => template_dir.to_path_buf()
- }
-}
-fn collect_template_dir(template_path: &PathBuf, _: &Path) -> CargoResult<Vec<Box<TemplateFile>>> {
- let mut templates = Vec::<Box<TemplateFile>>::new();
- // For every file found inside the given template directory, compile it as a handlebars
- // template and render it with the above data to a new file inside the target directory
- try!(walk_template_dir(&template_path, &mut |entry| {
- let entry_path = entry.path();
- let dest_file_name = PathBuf::from(try!(entry_path.strip_prefix(&template_path)
- .chain_error(|| {
- human(format!("entry is somehow not a subpath \
- of the directory being walked."))
- })));
- templates.push(Box::new(InputFileTemplateFile::new(entry_path,
- dest_file_name.to_path_buf())));
- Ok(())
- }));
- Ok(templates)
+ Ok(())
}
fn get_environment_variable(variables: &[&str] ) -> Option<String>{
}
None => None
};
-
Ok(CargoNewConfig {
name: name,
email: email,
})
}
-/// Recursively list directory contents under `dir`, only visiting files.
-///
-/// This will also filter out files & files types which we don't want to
-/// try generate templates for. Image files, for instance.
-///
-/// It also filters out certain files & file types, as we don't want t
-///
-/// We use this instead of std::fs::walk_dir as it is marked as unstable for now
-///
-/// This is a modified version of the example at:
-/// http://doc.rust-lang.org/std/fs/fn.read_dir.html
-fn walk_template_dir(dir: &Path, cb: &mut FnMut(DirEntry) -> CargoResult<()>) -> CargoResult<()> {
- let attr = try!(fs::metadata(&dir));
- let ignore_files = vec![".gitignore"];
-
- if !attr.is_dir() {
- return Ok(());
- }
- for entry in try!(fs::read_dir(dir)) {
- let entry = try!(entry);
- let attr = try!(fs::metadata(&entry.path()));
- if attr.is_dir() {
- if let Some(ref path_str) = entry.path().to_str() {
- if !&path_str.contains(".git") {
- try!(walk_template_dir(&entry.path(), cb));
- }
- }
- } else {
- if let Some(file_name) = entry.path().file_name() {
- if ignore_files.contains(&file_name.to_str().unwrap()) {
- continue
- }
- }
- try!(cb(entry));
- }
- }
- Ok(())
-}
-
-/// Create a generic template
-///
-/// This consists of a Cargo.toml, and a src directory.
-fn create_generic_template() -> Vec<Box<TemplateFile>> {
- let template_file = Box::new(InMemoryTemplateFile::new(PathBuf::from("Cargo.toml"),
- String::from(r#"[package]
-name = "{{name}}"
-version = "0.1.0"
-authors = [{{toml-escape author}}]
-
-[dependencies]
-"#)));
- vec![template_file]
-}
-
-/// Create a new "lib" project
-fn create_lib_template() -> Vec<Box<TemplateFile>> {
- let mut template_files = create_generic_template();
- let lib_file = Box::new(InMemoryTemplateFile::new(PathBuf::from("src/lib.rs"),
- String::from(r#"#[cfg(test)]
-mod tests {
- #[test]
- fn it_works() {
- }
-}
-"#)));
- template_files.push(lib_file);
- template_files
-}
-
-/// Create a new "bin" project
-fn create_bin_template() -> Vec<Box<TemplateFile>> {
- let mut template_files = create_generic_template();
- let main_file = Box::new(InMemoryTemplateFile::new(PathBuf::from("src/main.rs"),
-String::from("fn main() {
- println!(\"Hello, world!\");
-}
-")));
- template_files.push(main_file);
- template_files
-}
-
#[cfg(test)]
mod tests {
use super::strip_rust_affixes;
-pub use self::utils::{GitRemote, GitDatabase, GitCheckout, GitRevision, fetch, clone};
+pub use self::utils::{GitRemote, GitDatabase, GitCheckout, GitRevision, fetch};
pub use self::source::{GitSource, canonicalize_url};
mod utils;
mod source;
Ok(())
})
}
-
-/// Clone a remote repository into a target directory. This is a simple utility function to get
-/// HEAD. When this is complete it should be equivalent to `git clone $url $target`
-pub fn clone(url: &str, target: &Path, config: &Config) -> CargoResult<()> {
- let repo = git2::Repository::init(target).chain_error(|| {
- human(format!("Failed to create template directory `{}`",
- target.display()))
- })?;
- let refspec = "refs/heads/*:refs/heads/*";
- fetch(&repo, url, refspec, &config).chain_error(|| {
- human(format!("failed to fecth `{}`", url))
- })?;
- let reference = "HEAD";
- let oid = repo.refname_to_id(reference)?;
- let object = repo.find_object(oid, None)?;
- repo.reset(&object, git2::ResetType::Hard, None)?;
- Ok(())
-}
use std::fmt;
use std::io;
use std::num;
-use std::path;
use std::process::{Output, ExitStatus};
use std::str;
use std::string;
use curl;
use git2;
-use handlebars;
+use rustc_serialize::json;
use semver;
use serde_json;
use term;
term::Error,
num::ParseIntError,
str::ParseBoolError,
- path::StripPrefixError,
- handlebars::TemplateRenderError,
- handlebars::RenderError,
}
impl From<string::ParseError> for Box<CargoError> {
impl CargoError for term::Error {}
impl CargoError for num::ParseIntError {}
impl CargoError for str::ParseBoolError {}
-impl CargoError for path::StripPrefixError {}
-impl CargoError for handlebars::TemplateRenderError {}
-impl CargoError for handlebars::RenderError {}
// =============================================================================
// Construction helpers
pub mod paths;
pub mod process_builder;
pub mod profile;
-pub mod template;
pub mod to_semver;
pub mod to_url;
pub mod toml;
+++ /dev/null
-use std::path::{Path, PathBuf};
-use std::fs::File;
-use std::io::{Read};
-
-use util::{CargoResult, human, ChainError};
-use url::Url;
-
-use handlebars::{Context, Helper, Handlebars, RenderContext, RenderError, html_escape};
-use tempdir::TempDir;
-use toml;
-
-/// toml_escape_helper quotes strings in templates when they are wrapped in
-/// {{#toml-escape <template-variable}}
-/// So if 'name' is "foo \"bar\"" then:
-/// {{name}} renders as 'foo "bar"'
-/// {{#toml-escape name}} renders as '"foo \"bar\""'
-pub fn toml_escape_helper(_: &Context,
- h: &Helper,
- _: &Handlebars,
- rc: &mut RenderContext) -> Result<(), RenderError> {
- if let Some(param) = h.param(0) {
- let txt = param.value().as_str().unwrap_or("").to_owned();
- let rendered = format!("{}", toml::Value::String(txt));
- try!(rc.writer.write_all(rendered.into_bytes().as_ref()));
- }
- Ok(())
-}
-
-/// html_escape_helper escapes strings in templates using html escaping rules.
-pub fn html_escape_helper(_: &Context,
- h: &Helper,
- _: &Handlebars,
- rc: &mut RenderContext) -> Result<(), RenderError> {
- if let Some(param) = h.param(0) {
- let rendered = html_escape(param.value().as_str().unwrap_or(""));
- try!(rc.writer.write_all(rendered.into_bytes().as_ref()));
- }
- Ok(())
-}
-
-/// Trait to hold information required for rendering templated files.
-pub trait TemplateFile {
- /// Path of the template output for the file being written.
- fn path(&self) -> &Path;
-
- /// Return the template string.
- fn template(&self) -> CargoResult<String>;
-}
-
-/// TemplateFile based on an input file.
-pub struct InputFileTemplateFile {
- input_path: PathBuf,
- output_path: PathBuf,
-}
-
-impl TemplateFile for InputFileTemplateFile {
- fn path(&self) -> &Path {
- &self.output_path
- }
-
- fn template(&self) -> CargoResult<String> {
- let mut template_str = String::new();
- let mut entry_file = try!(File::open(&self.input_path).chain_error(|| {
- human(format!("Failed to open file for templating: {}", self.input_path.display()))
- }));
- try!(entry_file.read_to_string(&mut template_str).chain_error(|| {
- human(format!("Failed to read file for templating: {}", self.input_path.display()))
- }));
- Ok(template_str)
- }
-}
-
-impl InputFileTemplateFile {
- pub fn new(input_path: PathBuf, output_path: PathBuf) -> InputFileTemplateFile {
- InputFileTemplateFile {
- input_path: input_path,
- output_path: output_path
- }
- }
-}
-
-/// An in memory template file for --bin or --lib.
-pub struct InMemoryTemplateFile {
- template_str: String,
- output_path: PathBuf,
-}
-
-impl TemplateFile for InMemoryTemplateFile {
- fn path(&self) -> &Path {
- &self.output_path
- }
-
- fn template(&self) -> CargoResult<String> {
- Ok(self.template_str.clone())
- }
-}
-
-impl InMemoryTemplateFile {
- pub fn new(output_path: PathBuf, template_str: String) -> InMemoryTemplateFile {
- InMemoryTemplateFile {
- template_str: template_str,
- output_path: output_path
- }
- }
-}
-
-pub enum TemplateDirectory{
- Temp(TempDir),
- Normal(PathBuf),
-}
-
-impl TemplateDirectory {
- pub fn path(&self) -> &Path {
- match *self {
- TemplateDirectory::Temp(ref tempdir) => tempdir.path(),
- TemplateDirectory::Normal(ref path) => path.as_path()
- }
- }
-}
-
-/// A listing of all the files that are part of the template.
-pub struct TemplateSet {
- pub template_dir: Option<TemplateDirectory>,
- pub template_files: Vec<Box<TemplateFile>>
-}
-
-// The type of template we will use.
-#[derive(Debug, Eq, PartialEq)]
-pub enum TemplateType {
- GitRepo(String),
- LocalDir(String),
- Builtin
-}
-
-/// Given a repository string and subdir, determine if this is a git repository, local file, or a
-/// built in template. Git only supports a few schemas, so anything that is not supported is
-/// treated as a local path. The supported schemes are:
-/// "git", "file", "http", "https", and "ssh"
-/// Also supported is an scp style syntax: git@domain.com:user/path
-pub fn get_template_type<'a>(repo: Option<&'a str>,
- subdir: Option<&'a str>) -> CargoResult<TemplateType> {
- match (repo, subdir) {
- (Some(repo_str), _) => {
- if let Ok(repo_url) = Url::parse(repo_str) {
- let supported_schemes = ["git", "file", "http", "https", "ssh"];
- if supported_schemes.contains(&repo_url.scheme()) {
- Ok(TemplateType::GitRepo(repo_url.into_string()))
- } else {
- Ok(TemplateType::LocalDir(String::from(repo_str)))
- }
- } else {
- Ok(TemplateType::LocalDir(String::from(repo_str)))
- }
- },
- (None, Some(_)) => Err(human("A template was given, but no template repository")),
- (None, None) => Ok(TemplateType::Builtin)
- }
-}
-
-
-#[cfg(test)]
-mod test {
- use std::collections::BTreeMap;
- use handlebars::Handlebars;
- use super::*;
-
- #[test]
- fn test_toml_escape_helper() {
- let mut handlebars = Handlebars::new();
- handlebars.register_helper("toml-escape", Box::new(toml_escape_helper));
- let mut data = BTreeMap::new();
- data.insert("name".to_owned(), "\"Iron\" Mike Tyson".to_owned());
- let result = handlebars.template_render("Hello, {{#toml-escape name}}{{/toml-escape}}", &data).unwrap();
- assert_eq!(result, "Hello, \"\\\"Iron\\\" Mike Tyson\"");
- }
-
- macro_rules! test_get_template_proto {
- ( $funcname:ident, $url:expr ) => {
- #[test]
- fn $funcname() {
- assert_eq!(get_template_type(Some($url), Some("foo")).unwrap(),
- TemplateType::GitRepo($url.to_owned()));
- assert_eq!(get_template_type(Some($url), Some("")).unwrap(),
- TemplateType::GitRepo($url.to_owned()));
- assert_eq!(get_template_type(Some($url), None).unwrap(),
- TemplateType::GitRepo($url.to_owned()));
- }
- }
- }
-
- test_get_template_proto!(test_get_template_http, "http://foo.com/user/repo");
- test_get_template_proto!(test_get_template_https, "https://foo.com/user/repo");
- test_get_template_proto!(test_get_template_git, "git://foo.com/user/repo");
- test_get_template_proto!(test_get_template_file, "file://foo.com/user/repo");
- test_get_template_proto!(test_get_template_ssh, "ssh://user@foo.com/repo");
- // SSH scp style repository access is not yet supported.
- //test_get_template_proto!(test_get_template_ssh_scp_style, "git@foo.com:user/repo");
-
- #[test]
- fn test_get_template_type_git_repo_bad_proto_is_localdir() {
- assert_eq!(get_template_type(Some("ftps://foo.com/user/repo"), None).unwrap(),
- TemplateType::LocalDir("ftps://foo.com/user/repo".to_owned()));
- }
-
- #[test]
- fn test_get_template_type_local_dir_abs() {
- assert_eq!(get_template_type(Some("/foo/user/repo"), Some("foo")).unwrap(),
- TemplateType::LocalDir("/foo/user/repo".to_owned()));
- assert_eq!(get_template_type(Some("/foo/user/repo"), Some("")).unwrap(),
- TemplateType::LocalDir("/foo/user/repo".to_owned()));
- assert_eq!(get_template_type(Some("/foo/user/repo"), None).unwrap(),
- TemplateType::LocalDir("/foo/user/repo".to_owned()));
- }
-
- // Windows paths can be parsed as URLs so make sure they are parsed as local directories.
- #[test]
- fn test_get_template_type_windows_path_is_localdir() {
- assert_eq!(get_template_type(Some(r#"C:\foo\user\repo"#), None).unwrap(),
- TemplateType::LocalDir(r#"C:\foo\user\repo"#.to_owned()));
- assert_eq!(get_template_type(Some(r#"C:/foo/user/repo"#), None).unwrap(),
- TemplateType::LocalDir(r#"C:/foo/user/repo"#.to_owned()));
- }
-
- #[test]
- fn test_get_template_type_local_dir_rel() {
- assert_eq!(get_template_type(Some("foo/user/repo"), Some("foo")).unwrap(),
- TemplateType::LocalDir("foo/user/repo".to_owned()));
- assert_eq!(get_template_type(Some("foo/user/repo"), Some("")).unwrap(),
- TemplateType::LocalDir("foo/user/repo".to_owned()));
- assert_eq!(get_template_type(Some("foo/user/repo"), None).unwrap(),
- TemplateType::LocalDir("foo/user/repo".to_owned()));
- }
-
- #[test]
- fn test_get_template_type_builtin() {
- assert_eq!(get_template_type(None, None).unwrap(), TemplateType::Builtin);
- }
-}
were making a library, we’d leave it off. This also initializes a new `git`
repository by default. If you don't want it to do that, pass `--vcs none`.
-You can also use your own template to scaffold cargo projects! See the
-[Templates](#templates) section for more details.
-
Let’s check out what Cargo has generated for us:
```shell
name = "hello_world"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
-
-[dependencies]
```
This is called a **manifest**, and it contains all of the metadata that Cargo
documentation](https://docs.travis-ci.com/user/languages/rust/) for more
information.
-# Templates
-
-Cargo uses the [handlebars](https://github.com/sunng87/handlebars-rust) library
-to compile the templates used to scaffold projects. By default, there are only
-two templates available, `bin` and `lib`. These are used by cargo to create the
-standard project structure.
-
-You can also specify other templates from which to scaffold your project. The
-`--template` argument to `cargo new` accepts either a path on your system, or a
-URL to a remote Git repository containing a project template.
-
-```
-# use the mytemplate template which is located in ~/.cargo/templates/mytemplate
-$ cargo new myproj --template ~/.cargo/mytemplates/mytemplate
-
-# download the template called mytemplate from your github package
-$ cargo new myproj --template http://github.com/you/mytemplate
-```
-
-If you have a collection of templates in a Git repository then you can use the
-`--template-subdir` option to specify the subdirectory containing the template
-you want to use.
-
-```
-# download the template project called mytemplates from your github package
-# and use the 'command-line-project' template.
-$ cargo new myproj --template http://github.com/you/mytemplate --template-subdir command-line-project
-```
-
-## Creating new templates
-
-A cargo template is just a folder containing one or more files. Usually, there
-is a `Cargo.toml` and a `src` directory. Each file in the template directory
-(aside from the contents of the .git directory) will be treated as a handlebars
-template. This means you can use handlebars variables wherever you want dynamic
-content, and cargo will render the proper values. Let's create a simple example.
-Create a new folder called `mytemplate`
-
-Add the following files:
-
-```toml
-# Cargo.toml
-[project]
-name = "{{name}}"
-version = "0.1.0"
-authors = [{{toml-escape author}}]
-```
-
-```rust
-// src/main.rs
-fn main() {
- println!("This is the {{name}} project!");
-}
-```
-
-Upload this to a public git repository and anyone can now use it to start their
-projects with this command:
-
-```
-$ cargo new proj --template http://your/project/repo
-```
-
-## Available variables
-
-The variables available for use are:
-
-- `name`: the name of the project
-- `authors`: the toml formatted name of the project author
-
-In the future, more variables may be added. Suggestions welcome!
-
-## Available templating functions
-
-The available templating functions are:
-
-- `toml-escape`: Escapes a string for use in a TOML file.
-- `html-escape`: Escapes a string for use in a HTML file.
-
-There is more documentation available on the [Handlebars
-website](http://handlebarsjs.com/) though keep in mind that [the Rust
-implementation of Handlebars](https://docs.rs/handlebars/0.24.1/handlebars/)
-isn't 100% compatible with the Javascript version.
-
# Further reading
Now that you have an overview of how to use cargo and have created your first crate, you may be interested in:
'--vcs:initialize a new repo with a given VCS:(git hg none)' \
'(-h, --help)'{-h,--help}'[show help message]' \
'--name=[set the resulting package name]' \
- '--template[template from which to scaffold your project]' \
'(-q, --quiet)'{-q,--quiet}'[no output printed to stdout]' \
'(-v, --verbose)'{-v,--verbose}'[use verbose output]' \
'--color=:colorization option:(auto always never)' \
local opt__locate_project="$opt_mani -h --help"
local opt__login="$opt_common $opt_lock --host"
local opt__metadata="$opt_common $opt_feat $opt_mani $opt_lock --format-version --no-deps"
- local opt__new="$opt_common $opt_lock --vcs --bin --lib --name --template"
+ local opt__new="$opt_common $opt_lock --vcs --bin --lib --name"
local opt__owner="$opt_common $opt_lock -a --add -r --remove -l --list --index --token"
local opt__package="$opt_common $opt_mani $opt_lock $opt_jobs --allow-dirty -l --list --no-verify --no-metadata"
local opt__pkgid="${opt__fetch} $opt_pkg"
use cargo::util::ProcessBuilder;
use cargotest::process;
-use cargotest::support::{execs, git, paths};
-use chrono::{Datelike,Local};
+use cargotest::support::{execs, paths};
use hamcrest::{assert_that, existing_file, existing_dir, is_not};
use tempdir::TempDir;
existing_file());
}
-#[test]
-fn simple_template() {
- let root = paths::root();
- fs::create_dir_all(&root.join("home/.cargo/templates/testtemplate/src")).unwrap();
- File::create(&root.join("home/.cargo/templates/testtemplate/Cargo.toml"))
- .unwrap().write_all(br#"[package]
-name = "{{name}}"
-version = "0.0.1"
-authors = ["{{author}}"]
-"#).unwrap();
- File::create(&root.join("home/.cargo/templates/testtemplate/src/main.rs"))
- .unwrap().write_all(br#"
-fn main () {
- println!("hello {{name}}");
-}
- "#).unwrap();
-
- assert_that(cargo_process("new").arg("--template-subdir").arg("testtemplate")
- .arg("--template")
- .arg(&root.join("home/.cargo/templates/"))
- .arg("foo")
- .env("USER", "foo"),
- execs().with_status(0).with_stderr("\
-[CREATED] library `foo` project
-"));
-
- assert_that(&paths::root().join("foo"), existing_dir());
- assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
- assert_that(&paths::root().join("foo/src/main.rs"), existing_file());
-
- let license = paths::root().join("foo/LICENSE");
- let mut contents = String::new();
- File::open(&license).unwrap().read_to_string(&mut contents).unwrap();
- assert!(contents.contains(&format!("(c) {} {}", Local::now().year(), "foo")));
-
- assert_that(cargo_process("build").cwd(&paths::root().join("foo")),
- execs().with_status(0));
- assert_that(&paths::root().join(&format!("foo/target/debug/foo{}",
- env::consts::EXE_SUFFIX)),
- existing_file());
-}
-
-#[test]
-fn git_template() {
- let git_project = git::new("template1", |project| {
- project
- .file("Cargo.toml", r#"[package]
-name = "{{name}}"
-version = "0.0.1"
-authors = ["{{author}}"]
- "#)
- .file("src/main.rs", r#"
- pub fn main() {
- println!("hello world");
- }
- "#)
- }).unwrap();
-
- assert_that(cargo_process("new").arg("--template").arg(git_project.url().as_str())
- .arg("foo")
- .env("USER", "foo"),
- execs().with_status(0));
-
- assert_that(&paths::root().join("foo"), existing_dir());
- assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
- assert_that(&paths::root().join("foo/src/main.rs"), existing_file());
-
- assert_that(cargo_process("build").cwd(&paths::root().join("foo")),
- execs().with_status(0));
- assert_that(&paths::root().join(&format!("foo/target/debug/foo{}",
- env::consts::EXE_SUFFIX)),
- existing_file());
-}
-
#[test]
fn both_lib_and_bin() {
let td = TempDir::new("cargo").unwrap();